import os
import time
import schedule
from openai import OpenAI
import ccxt
import pandas as pd
from datetime import datetime
import json
import re
import requests
import logging
from logging.handlers import RotatingFileHandler
from dataclasses import dataclass, field
from copy import deepcopy
from typing import Dict, List, Optional, Any
from dotenv import load_dotenv

load_dotenv()

DEFAULT_RISK_PROFILE = os.getenv('TRADE_RISK_PROFILE', 'aggressive').lower()
try:
    DEFAULT_LIMIT_SAFETY_RATIO = float(os.getenv('TRADE_LIMIT_SAFETY', '0.985'))
except ValueError:
    DEFAULT_LIMIT_SAFETY_RATIO = 0.985

# 配置日志系统
def setup_logger():
    """配置日志系统 - 同时输出到文件和控制台"""
    # 创建 logs 目录
    log_dir = 'logs'
    if not os.path.exists(log_dir):
        try:
            os.makedirs(log_dir, exist_ok=True)
        except PermissionError:
            # 如果无法创建logs目录，只使用控制台输出
            print("⚠️ 无法创建logs目录，将只输出到控制台")
    
    # 创建 logger
    logger = logging.getLogger('OKXTradeBot')
    logger.setLevel(logging.DEBUG)
    
    # 清除已有的 handlers（避免重复）
    logger.handlers.clear()
    
    # 文件日志格式（详细）
    file_formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    
    # 控制台日志格式（简洁）
    console_formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(message)s',
        datefmt='%H:%M:%S'
    )
    
    # 尝试创建文件处理器
    try:
        # 文件处理器 - 主日志（自动轮转，最大 10MB，保留 5 个备份）
        file_handler = RotatingFileHandler(
            os.path.join(log_dir, 'trade_bot.log'),
            maxBytes=10*1024*1024,  # 10MB
            backupCount=5,
            encoding='utf-8'
        )
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(file_formatter)
        logger.addHandler(file_handler)
        
        # 文件处理器 - 错误日志
        error_handler = RotatingFileHandler(
            os.path.join(log_dir, 'error.log'),
            maxBytes=5*1024*1024,  # 5MB
            backupCount=3,
            encoding='utf-8'
        )
        error_handler.setLevel(logging.ERROR)
        error_handler.setFormatter(file_formatter)
        logger.addHandler(error_handler)
        
        # 文件处理器 - 交易日志
        trade_handler = RotatingFileHandler(
            os.path.join(log_dir, 'trade.log'),
            maxBytes=10*1024*1024,  # 10MB
            backupCount=10,
            encoding='utf-8'
        )
        trade_handler.setLevel(logging.INFO)
        trade_handler.setFormatter(file_formatter)
        # 添加过滤器，只记录交易相关日志
        trade_handler.addFilter(lambda record: 'TRADE' in record.getMessage() or 'Signal' in record.getMessage())
        logger.addHandler(trade_handler)
        
    except (PermissionError, OSError) as e:
        print(f"⚠️ 无法创建日志文件，将只输出到控制台: {e}")
    
    # 控制台处理器（始终可用）
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)
    
    return logger

# 初始化全局 logger
logger = setup_logger()
PERFORMANCE_LOG_PATH = os.path.join('logs', 'trade_history.jsonl')

# 自定义 print 函数，同时输出到控制台和日志文件
_original_print = print
def print(*args, **kwargs):
    """重写 print 函数，同时记录到日志"""
    # 获取打印内容
    message = ' '.join(str(arg) for arg in args)
    
    # 根据内容判断日志级别
    if '❌' in message or '失败' in message or 'Error' in message or 'error' in message:
        logger.error(message)
    elif '⚠️' in message or '警告' in message or 'Warning' in message:
        logger.warning(message)
    elif 'TRADE' in message or 'Signal' in message or '信号' in message or '交易' in message:
        logger.info(f"[TRADE] {message}")  # 交易日志添加标记
    elif '✅' in message or '成功' in message:
        logger.info(message)
    elif message.startswith('='*10) or message.startswith('-'*10):
        logger.debug(message)  # 分隔线用 debug 级别
    else:
        logger.info(message)

# 初始化DeepSeek客户端
deepseek_client = OpenAI(
    api_key=os.getenv('DEEPSEEK_API_KEY'),
    base_url="https://api.deepseek.com"
)

# 初始化OKX交易所（延迟初始化，根据模式选择）
exchange = None

# 交易参数配置 - 支持多个符号
TRADE_CONFIG = {
    'symbols': [
        {'symbol': 'BTC/USDT:USDT', 'amount': 0.15},  # BTC (~1650 USDT保证金 @110k, 10x杠杆)
        {'symbol': 'ETH/USDT:USDT', 'amount': 1.5},   # ETH (~525 USDT保证金 @3.5k, 10x杠杆)
        {'symbol': 'SOL/USDT:USDT', 'amount': 30},     # SOL (~450 USDT保证金 @150, 10x杠杆)
        {'symbol': 'DOGE/USDT:USDT', 'amount': 15000}  # DOGE (~600 USDT保证金, 10x杠杆)
    ],
    'leverage': 10,  # 杠杆倍数（全局）
    'timeframe': '15m',  # 使用15分钟K线（全局）
    'use_demo_api': True,  # 使用OKX模拟盘API（True=模拟盘, False=实盘）
    'max_position_ratio': 0.6,  # 单个交易对最大持仓比例（60%的可用资金）
    'total_position_ratio': 0.95,  # 总持仓比例上限（95%的可用资金）
    'min_balance_reserve': 200,  # 最小保留余额（USDT）
    'data_points': 96,  # 24小时数据（96根15分钟K线）
    'analysis_periods': {
        'short_term': 20,  # 短期均线
        'medium_term': 50,  # 中期均线
        'long_term': 96  # 长期趋势
    },
    'risk_profile': DEFAULT_RISK_PROFILE,
    'limit_safety_ratio': DEFAULT_LIMIT_SAFETY_RATIO,
    # Telegram 通知配置
    'telegram_enabled': True,  # 是否启用 Telegram 通知
    'telegram_bot_token': os.getenv('TELEGRAM_BOT_TOKEN'),
    'telegram_chat_id': os.getenv('TELEGRAM_CHAT_ID'),
}

RISK_PROFILE_PRESETS = {
    'conservative': {
        'amount_multiplier': 0.6,
        'max_position_ratio': 0.4,
        'total_position_ratio': 0.8,
        'min_balance_reserve': 400,
        'limit_safety_ratio': 0.98,
        'analysis_periods': {
            'short_term': 30,
            'medium_term': 80,
            'long_term': 160,
        },
    },
    'balanced': {
        'amount_multiplier': 1.0,
        'max_position_ratio': 0.6,
        'total_position_ratio': 0.95,
        'min_balance_reserve': 200,
        'limit_safety_ratio': 0.985,
    },
    'aggressive': {
        'amount_multiplier': 1.25,
        'max_position_ratio': 0.75,
        'total_position_ratio': 0.98,
        'min_balance_reserve': 120,
        'limit_safety_ratio': 0.995,
        'analysis_periods': {
            'short_term': 14,
            'medium_term': 40,
            'long_term': 96,
        },
    },
}


def apply_risk_profile_settings(
    config: dict,
    preferred_profile: Optional[str] = None,
):
    """根据风险档位调节资金参数与下单安全边际。"""
    cfg = deepcopy(config)
    profile_key = (preferred_profile or cfg.get('risk_profile') or 'balanced').lower()
    profile_settings = RISK_PROFILE_PRESETS.get(profile_key)

    if not profile_settings:
        cfg['risk_profile'] = profile_key
        cfg.setdefault('limit_safety_ratio', config.get('limit_safety_ratio', DEFAULT_LIMIT_SAFETY_RATIO))
        return cfg, profile_key

    multiplier = profile_settings.get('amount_multiplier', 1.0)
    if multiplier != 1.0:
        adjusted_symbols = []
        for sym in cfg.get('symbols', []):
            adjusted_symbols.append({**sym, 'amount': round(sym['amount'] * multiplier, 8)})
        cfg['symbols'] = adjusted_symbols

    override_fields = (
        'max_position_ratio',
        'total_position_ratio',
        'min_balance_reserve',
        'leverage',
        'timeframe',
        'data_points',
        'limit_safety_ratio',
    )
    for field in override_fields:
        if field in profile_settings:
            cfg[field] = profile_settings[field]

    if 'analysis_periods' in profile_settings:
        merged_periods = cfg.get('analysis_periods', {}).copy()
        merged_periods.update(profile_settings['analysis_periods'])
        cfg['analysis_periods'] = merged_periods

    cfg['risk_profile'] = profile_key
    cfg.setdefault('limit_safety_ratio', DEFAULT_LIMIT_SAFETY_RATIO)
    return cfg, profile_key


TRADE_CONFIG, ACTIVE_RISK_PROFILE = apply_risk_profile_settings(
    TRADE_CONFIG,
    TRADE_CONFIG.get('risk_profile')
)

# 全局变量存储历史数据，按符号分开
price_history = {sym['symbol']: [] for sym in TRADE_CONFIG['symbols']}
signal_history = {sym['symbol']: [] for sym in TRADE_CONFIG['symbols']}
positions = {sym['symbol']: None for sym in TRADE_CONFIG['symbols']}


def log_trade_event(event: dict):
    try:
        os.makedirs(os.path.dirname(PERFORMANCE_LOG_PATH), exist_ok=True)
        with open(PERFORMANCE_LOG_PATH, 'a', encoding='utf-8') as f:
            f.write(json.dumps(event, ensure_ascii=False) + '\n')
    except Exception as exc:
        logger.warning(f"记录交易事件失败: {exc}")


@dataclass
class StrategyState:
    """运行时状态，用于多账户或自定义配置"""
    trade_config: dict
    exchange: Any
    telegram_config: Optional[dict] = None
    sandbox: bool = True
    account_id: Optional[str] = None
    price_history: Dict[str, List[float]] = field(default_factory=dict)
    signal_history: Dict[str, List[dict]] = field(default_factory=dict)
    positions: Dict[str, Optional[dict]] = field(default_factory=dict)
    risk_profile: str = 'balanced'

    def __post_init__(self):
        symbols = [sym['symbol'] for sym in self.trade_config.get('symbols', [])]
        if not self.price_history:
            self.price_history = {sym: [] for sym in symbols}
        if not self.signal_history:
            self.signal_history = {sym: [] for sym in symbols}
        if not self.positions:
            self.positions = {sym: None for sym in symbols}
        self.telegram_config = self.telegram_config or {}
        self.risk_profile = self.trade_config.get('risk_profile', self.risk_profile)


def create_strategy_state(
    trade_config: dict,
    exchange_instance,
    telegram_config: Optional[dict] = None,
    sandbox: Optional[bool] = None,
    account_id: Optional[str] = None,
) -> StrategyState:
    """为多账户场景创建独立的策略运行时"""
    cfg = deepcopy(trade_config)
    cfg, applied_profile = apply_risk_profile_settings(cfg, cfg.get('risk_profile'))

    # 填充默认值
    defaults = {
        'symbols': TRADE_CONFIG.get('symbols', []),
        'leverage': TRADE_CONFIG.get('leverage', 1),
        'timeframe': TRADE_CONFIG.get('timeframe', '15m'),
        'max_position_ratio': TRADE_CONFIG.get('max_position_ratio', 0.6),
        'total_position_ratio': TRADE_CONFIG.get('total_position_ratio', 0.95),
        'min_balance_reserve': TRADE_CONFIG.get('min_balance_reserve', 200),
        'data_points': TRADE_CONFIG.get('data_points', 96),
        'analysis_periods': TRADE_CONFIG.get(
            'analysis_periods',
            {'short_term': 20, 'medium_term': 50, 'long_term': 96}
        ),
    }

    for key, value in defaults.items():
        cfg.setdefault(key, deepcopy(value))

    if sandbox is None:
        sandbox = cfg.get('use_demo_api', True)
    else:
        cfg['use_demo_api'] = sandbox

    return StrategyState(
        trade_config=cfg,
        exchange=exchange_instance,
        telegram_config=deepcopy(telegram_config) if telegram_config else {},
        sandbox=sandbox,
        account_id=account_id,
        risk_profile=cfg.get('risk_profile', 'balanced'),
    )


def send_telegram_message(message, parse_mode='HTML', bot_token=None, chat_id=None):
    """发送 Telegram 消息通知"""
    token = bot_token
    chat = chat_id

    if token is None or chat is None:
        if not TRADE_CONFIG.get('telegram_enabled', False):
            return
        token = token or TRADE_CONFIG.get('telegram_bot_token')
        chat = chat or TRADE_CONFIG.get('telegram_chat_id')

    if not token or not chat:
        logger.warning("Telegram 配置不完整，跳过推送")
        return
    
    try:
        url = f"https://api.telegram.org/bot{token}/sendMessage"
        payload = {
            'chat_id': chat,
            'text': message,
            'parse_mode': parse_mode
        }
        response = requests.post(url, json=payload, timeout=10)
        if response.status_code == 200:
            logger.info("✅ Telegram 消息已发送")
        else:
            logger.warning(f"Telegram 发送失败: {response.status_code}")
    except Exception as e:
        logger.warning(f"Telegram 发送异常: {e}")


def format_trade_message(
    symbol,
    signal_data,
    price_data,
    operation_type,
    amount,
    required_margin,
    balance_info=None,
    trade_config=None,
    sandbox=None,
):
    """格式化交易消息"""
    # 移除 :USDT 后缀使显示更简洁
    clean_symbol = symbol.replace('/USDT:USDT', '/USDT')

    cfg = trade_config or TRADE_CONFIG
    leverage = cfg.get('leverage', TRADE_CONFIG.get('leverage', 1))

    # 判断环境
    if sandbox is None:
        sandbox = cfg.get('use_demo_api', True)
    env_tag = "🔵 模拟盘" if sandbox else "🔴 实盘"
    
    # 信号emoji
    signal_emoji = {
        'BUY': '📈',
        'SELL': '📉',
        'HOLD': '⏸'
    }.get(signal_data['signal'], '❓')
    
    # 信心程度emoji
    confidence_emoji = {
        'HIGH': '🟢',
        'MEDIUM': '🟡',
        'LOW': '🔴'
    }.get(signal_data['confidence'], '⚪️')
    
    message = f"""
{env_tag} <b>交易信号</b>

{signal_emoji} <b>{clean_symbol}</b>
━━━━━━━━━━━━━━━━
📊 信号: <b>{signal_data['signal']}</b>
{confidence_emoji} 信心: {signal_data['confidence']}
⚙️ 操作: {operation_type}

💰 <b>价格信息</b>
当前价: ${price_data['price']:,.2f}
止损价: ${signal_data['stop_loss']:,.2f}
止盈价: ${signal_data['take_profit']:,.2f}

📦 <b>交易详情</b>
数量: {amount}
保证金: {required_margin:.2f} USDT
杠杆: {leverage}x
"""
    
    if balance_info:
        risk_text = balance_info.get('risk_profile')
        message += f"""
💼 <b>账户状态</b>
可用: {balance_info['free']:.2f} USDT
总额: {balance_info['total']:.2f} USDT
持仓: {balance_info['margin']:.2f} USDT
使用率: {balance_info['usage']:.1f}%
"""
        if risk_text:
            message += f"风险档: {risk_text}\n"
    
    message += f"""
📝 <b>分析理由</b>
{signal_data['reason']}

⏰ 时间: {price_data['timestamp']}
"""
    
    return message


def _telegram_enabled(telegram_config=None):
    if telegram_config is None:
        return TRADE_CONFIG.get('telegram_enabled', False)
    return telegram_config.get('enabled', False)


def _resolve_telegram_credentials(telegram_config=None):
    if telegram_config is None:
        return TRADE_CONFIG.get('telegram_bot_token'), TRADE_CONFIG.get('telegram_chat_id')
    return telegram_config.get('bot_token'), telegram_config.get('chat_id')


def send_trade_notification(symbol, signal_data, price_data, operation_type,
                            amount, required_margin, balance_info=None,
                            trade_config=None, telegram_config=None, sandbox=None):
    """发送交易通知到 Telegram"""
    if not _telegram_enabled(telegram_config):
        return

    cfg = trade_config or TRADE_CONFIG
    message = format_trade_message(
        symbol,
        signal_data,
        price_data,
        operation_type,
        amount,
        required_margin,
        balance_info,
        trade_config=cfg,
        sandbox=sandbox,
    )
    bot_token, chat_id = _resolve_telegram_credentials(telegram_config)
    send_telegram_message(message, bot_token=bot_token, chat_id=chat_id)


def send_error_notification(error_msg, symbol=None, trade_config=None,
                            telegram_config=None, sandbox=None,
                            account_id: str | None = None):
    """发送错误通知到 Telegram"""
    if not _telegram_enabled(telegram_config):
        return

    cfg = trade_config or TRADE_CONFIG
    if sandbox is None:
        sandbox = cfg.get('use_demo_api', True)

    env_tag = "🔵 模拟盘" if sandbox else "🔴 实盘"
    symbol_text = f" - {symbol.replace('/USDT:USDT', '/USDT')}" if symbol else ""
    account_text = f"\n账户: {account_id}" if account_id else ""
    
    message = f"""
{env_tag} <b>⚠️ 交易错误</b>{symbol_text}

{error_msg}

⏰ 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
{account_text}
"""
    bot_token, chat_id = _resolve_telegram_credentials(telegram_config)
    send_telegram_message(message, bot_token=bot_token, chat_id=chat_id)


def send_position_update(symbol, position, pnl=None, trade_config=None,
                         telegram_config=None, sandbox=None):
    """发送持仓更新通知"""
    if not _telegram_enabled(telegram_config):
        return

    cfg = trade_config or TRADE_CONFIG
    if sandbox is None:
        sandbox = cfg.get('use_demo_api', True)

    env_tag = "🔵 模拟盘" if sandbox else "🔴 实盘"
    clean_symbol = symbol.replace('/USDT:USDT', '/USDT')
    
    side_emoji = "📈" if position['side'] == 'long' else "📉"
    pnl_emoji = "✅" if pnl and pnl > 0 else "❌" if pnl and pnl < 0 else "⚪️"
    
    message = f"""
{env_tag} <b>持仓更新</b>

{side_emoji} <b>{clean_symbol}</b>
━━━━━━━━━━━━━━━━
方向: {position['side'].upper()}
数量: {position['size']}
入场价: ${position['entry_price']:,.2f}
杠杆: {position['leverage']}x
"""
    
    if pnl is not None:
        message += f"\n{pnl_emoji} 未实现盈亏: {pnl:+.2f} USDT\n"

    message += f"\n⏰ 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
    
    bot_token, chat_id = _resolve_telegram_credentials(telegram_config)
    send_telegram_message(message, bot_token=bot_token, chat_id=chat_id)


def init_exchange(use_demo=True):
    """初始化OKX交易所，根据模式选择模拟盘或实盘"""
    global exchange
    
    # 根据模式选择不同的环境变量
    if use_demo:
        api_key = os.getenv('OKX_DEMO_API_KEY') or os.getenv('OKX_API_KEY')
        secret = os.getenv('OKX_DEMO_SECRET') or os.getenv('OKX_SECRET')
        password = os.getenv('OKX_DEMO_PASSWORD') or os.getenv('OKX_PASSWORD')
        print("📍 使用 OKX 模拟盘 API")
    else:
        api_key = os.getenv('OKX_API_KEY')
        secret = os.getenv('OKX_SECRET')
        password = os.getenv('OKX_PASSWORD')
        print("📍 使用 OKX 实盘 API")
    
    exchange = ccxt.okx({
        'apiKey': api_key,
        'secret': secret,
        'password': password,
        'options': {
            'defaultType': 'swap',  # OKX使用swap表示永续合约
        },
    })
    
    # 设置模拟盘模式
    if use_demo:
        exchange.set_sandbox_mode(True)
        print("✅ 已启用 OKX 模拟盘模式（Sandbox）")
    else:
        exchange.set_sandbox_mode(False)
        print("⚠️  已启用 OKX 实盘模式")
    
    return exchange


def setup_exchange():
    """设置交易所参数"""
    try:
        # 首先检查并设置账户模式
        try:
            # 获取当前账户配置
            account_config = exchange.private_get_account_config()
            current_mode = account_config.get('data', [{}])[0].get('acctLv', '')
            
            print(f"当前账户模式: {current_mode}")
            
            # 如果不是保证金模式，尝试切换
            # acctLv: 1=简单交易, 2=单币种保证金, 3=跨币种保证金, 4=组合保证金
            if current_mode == '1':
                print("检测到简单交易模式，正在切换到单币种保证金模式...")
                try:
                    # 尝试切换到单币种保证金模式
                    result = exchange.private_post_account_set_account_level({
                        'acctLv': '2'  # 单币种保证金模式
                    })
                    print("✅ 已切换到单币种保证金模式")
                except Exception as e:
                    logger.warning(f"自动切换账户模式失败: {e}")
                    print("⚠️ 无法自动切换账户模式，请手动在 OKX 网站切换：")
                    print("   1. 登录 OKX 模拟交易/实盘")
                    print("   2. 点击右上角头像 -> 账户模式")
                    print("   3. 切换到'单币种保证金模式'或'跨币种保证金模式'")
                    print("   4. 重新启动程序")
                    return False
        except Exception as e:
            logger.debug(f"检查账户模式时出错（可忽略）: {e}")
        
        # 为每个符号设置杠杆
        for sym_config in TRADE_CONFIG['symbols']:
            symbol = sym_config['symbol']
            try:
                exchange.set_leverage(
                    TRADE_CONFIG['leverage'],
                    symbol,
                    {'mgnMode': 'cross'}  # 全仓模式
                )
                print(f"设置 {symbol} 杠杆倍数: {TRADE_CONFIG['leverage']}x")
            except Exception as e:
                logger.warning(f"设置 {symbol} 杠杆失败: {e}")
                print(f"⚠️ 设置 {symbol} 杠杆失败，将使用默认杠杆")

        # 获取余额
        balance = exchange.fetch_balance()
        usdt_balance = balance['USDT']['free']
        print(f"当前USDT余额: {usdt_balance:.2f}")

        return True
    except Exception as e:
        print(f"❌ 交易所设置失败: {e}")
        logger.error(f"交易所设置失败: {e}")
        return False


def calculate_technical_indicators(df):
    """计算技术指标 - 来自第一个策略"""
    try:
        # 移动平均线
        df['sma_5'] = df['close'].rolling(window=5, min_periods=1).mean()
        df['sma_20'] = df['close'].rolling(window=20, min_periods=1).mean()
        df['sma_50'] = df['close'].rolling(window=50, min_periods=1).mean()

        # 指数移动平均线
        df['ema_12'] = df['close'].ewm(span=12).mean()
        df['ema_26'] = df['close'].ewm(span=26).mean()
        df['macd'] = df['ema_12'] - df['ema_26']
        df['macd_signal'] = df['macd'].ewm(span=9).mean()
        df['macd_histogram'] = df['macd'] - df['macd_signal']

        # 相对强弱指数 (RSI)
        delta = df['close'].diff()
        gain = (delta.where(delta > 0, 0)).rolling(14).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
        rs = gain / loss
        df['rsi'] = 100 - (100 / (1 + rs))

        # 布林带
        df['bb_middle'] = df['close'].rolling(20).mean()
        bb_std = df['close'].rolling(20).std()
        df['bb_upper'] = df['bb_middle'] + (bb_std * 2)
        df['bb_lower'] = df['bb_middle'] - (bb_std * 2)
        df['bb_position'] = (df['close'] - df['bb_lower']) / (df['bb_upper'] - df['bb_lower'])

        # 成交量均线
        df['volume_ma'] = df['volume'].rolling(20).mean()
        df['volume_ratio'] = df['volume'] / df['volume_ma']

        # 支撑阻力位
        df['resistance'] = df['high'].rolling(20).max()
        df['support'] = df['low'].rolling(20).min()

        # 填充NaN值
        df = df.bfill().ffill()

        return df
    except Exception as e:
        print(f"技术指标计算失败: {e}")
        return df


def get_support_resistance_levels(df, lookback=20):
    """计算支撑阻力位"""
    try:
        recent_high = df['high'].tail(lookback).max()
        recent_low = df['low'].tail(lookback).min()
        current_price = df['close'].iloc[-1]

        resistance_level = recent_high
        support_level = recent_low

        # 动态支撑阻力（基于布林带）
        bb_upper = df['bb_upper'].iloc[-1]
        bb_lower = df['bb_lower'].iloc[-1]

        return {
            'static_resistance': resistance_level,
            'static_support': support_level,
            'dynamic_resistance': bb_upper,
            'dynamic_support': bb_lower,
            'price_vs_resistance': ((resistance_level - current_price) / current_price) * 100,
            'price_vs_support': ((current_price - support_level) / support_level) * 100
        }
    except Exception as e:
        print(f"支撑阻力计算失败: {e}")
        return {}


def get_market_trend(df):
    """判断市场趋势"""
    try:
        current_price = df['close'].iloc[-1]

        # 多时间框架趋势分析
        trend_short = "上涨" if current_price > df['sma_20'].iloc[-1] else "下跌"
        trend_medium = "上涨" if current_price > df['sma_50'].iloc[-1] else "下跌"

        # MACD趋势
        macd_trend = "bullish" if df['macd'].iloc[-1] > df['macd_signal'].iloc[-1] else "bearish"

        # 综合趋势判断
        if trend_short == "上涨" and trend_medium == "上涨":
            overall_trend = "强势上涨"
        elif trend_short == "下跌" and trend_medium == "下跌":
            overall_trend = "强势下跌"
        else:
            overall_trend = "震荡整理"

        return {
            'short_term': trend_short,
            'medium_term': trend_medium,
            'macd': macd_trend,
            'overall': overall_trend,
            'rsi_level': df['rsi'].iloc[-1]
        }
    except Exception as e:
        print(f"趋势分析失败: {e}")
        return {}


def get_ohlcv_enhanced(symbol, state: StrategyState | None = None,
                       trade_config: dict | None = None,
                       exchange_instance=None):
    """增强版：获取K线数据并计算技术指标"""
    try:
        cfg = trade_config or (state.trade_config if state else TRADE_CONFIG)
        ex = exchange_instance or (state.exchange if state else exchange)
        if ex is None:
            print("获取K线数据失败：交易所未初始化")
            return None

        # 获取K线数据
        ohlcv = ex.fetch_ohlcv(symbol, cfg['timeframe'],
                               limit=cfg.get('data_points', TRADE_CONFIG.get('data_points', 96)))

        df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
        df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')

        # 计算技术指标
        df = calculate_technical_indicators(df)

        current_data = df.iloc[-1]
        previous_data = df.iloc[-2]

        # 获取技术分析数据
        trend_analysis = get_market_trend(df)
        levels_analysis = get_support_resistance_levels(df)

        return {
            'price': current_data['close'],
            'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            'high': current_data['high'],
            'low': current_data['low'],
            'volume': current_data['volume'],
            'timeframe': cfg.get('timeframe', TRADE_CONFIG.get('timeframe', '15m')),
            'price_change': ((current_data['close'] - previous_data['close']) / previous_data['close']) * 100,
            'kline_data': df[['timestamp', 'open', 'high', 'low', 'close', 'volume']].tail(10).to_dict('records'),
            'technical_data': {
                'sma_5': current_data.get('sma_5', 0),
                'sma_20': current_data.get('sma_20', 0),
                'sma_50': current_data.get('sma_50', 0),
                'rsi': current_data.get('rsi', 0),
                'macd': current_data.get('macd', 0),
                'macd_signal': current_data.get('macd_signal', 0),
                'macd_histogram': current_data.get('macd_histogram', 0),
                'bb_upper': current_data.get('bb_upper', 0),
                'bb_lower': current_data.get('bb_lower', 0),
                'bb_position': current_data.get('bb_position', 0),
                'volume_ratio': current_data.get('volume_ratio', 0)
            },
            'trend_analysis': trend_analysis,
            'levels_analysis': levels_analysis,
            'full_data': df
        }
    except Exception as e:
        print(f"获取 {symbol} 增强K线数据失败: {e}")
        return None


def generate_technical_analysis_text(price_data):
    """生成技术分析文本"""
    if 'technical_data' not in price_data:
        return "技术指标数据不可用"

    tech = price_data['technical_data']
    trend = price_data.get('trend_analysis', {})
    levels = price_data.get('levels_analysis', {})

    # 检查数据有效性
    def safe_float(value, default=0):
        return float(value) if value and pd.notna(value) else default

    analysis_text = f"""
    【技术指标分析】
    📈 移动平均线:
    - 5周期: {safe_float(tech['sma_5']):.2f} | 价格相对: {(price_data['price'] - safe_float(tech['sma_5'])) / safe_float(tech['sma_5']) * 100:+.2f}%
    - 20周期: {safe_float(tech['sma_20']):.2f} | 价格相对: {(price_data['price'] - safe_float(tech['sma_20'])) / safe_float(tech['sma_20']) * 100:+.2f}%
    - 50周期: {safe_float(tech['sma_50']):.2f} | 价格相对: {(price_data['price'] - safe_float(tech['sma_50'])) / safe_float(tech['sma_50']) * 100:+.2f}%

    🎯 趋势分析:
    - 短期趋势: {trend.get('short_term', 'N/A')}
    - 中期趋势: {trend.get('medium_term', 'N/A')}
    - 整体趋势: {trend.get('overall', 'N/A')}
    - MACD方向: {trend.get('macd', 'N/A')}

    📊 动量指标:
    - RSI: {safe_float(tech['rsi']):.2f} ({'超买' if safe_float(tech['rsi']) > 70 else '超卖' if safe_float(tech['rsi']) < 30 else '中性'})
    - MACD: {safe_float(tech['macd']):.4f}
    - 信号线: {safe_float(tech['macd_signal']):.4f}

    🎚️ 布林带位置: {safe_float(tech['bb_position']):.2%} ({'上部' if safe_float(tech['bb_position']) > 0.7 else '下部' if safe_float(tech['bb_position']) < 0.3 else '中部'})

    💰 关键水平:
    - 静态阻力: {safe_float(levels.get('static_resistance', 0)):.2f}
    - 静态支撑: {safe_float(levels.get('static_support', 0)):.2f}
    """
    return analysis_text


def get_current_position(symbol, exchange_instance=None):
    """获取当前持仓情况 - OKX版本"""
    try:
        ex = exchange_instance or exchange
        if ex is None:
            print("获取持仓失败：交易所未初始化")
            return None

        positions_response = ex.fetch_positions([symbol])

        for pos in positions_response:
            if pos['symbol'] == symbol:
                contracts = float(pos['contracts']) if pos['contracts'] else 0

                if contracts > 0:
                    return {
                        'side': pos['side'],  # 'long' or 'short'
                        'size': contracts,
                        'entry_price': float(pos['entryPrice']) if pos['entryPrice'] else 0,
                        'unrealized_pnl': float(pos['unrealizedPnl']) if pos['unrealizedPnl'] else 0,
                        'leverage': float(pos['leverage']) if pos['leverage'] else TRADE_CONFIG['leverage'],
                        'symbol': pos['symbol']
                    }

        return None

    except Exception as e:
        print(f"获取 {symbol} 持仓失败: {e}")
        import traceback
        traceback.print_exc()
        return None


def safe_json_parse(json_str):
    """安全解析JSON，处理格式不规范的情况"""
    try:
        return json.loads(json_str)
    except json.JSONDecodeError:
        try:
            # 修复常见的JSON格式问题
            json_str = json_str.replace("'", '"')
            json_str = re.sub(r'(\w+):', r'"\1":', json_str)
            json_str = re.sub(r',\s*}', '}', json_str)
            json_str = re.sub(r',\s*]', ']', json_str)
            return json.loads(json_str)
        except json.JSONDecodeError as e:
            print(f"JSON解析失败，原始内容: {json_str}")
            print(f"错误详情: {e}")
            return None


def create_fallback_signal(price_data):
    """创建备用交易信号"""
    return {
        "signal": "HOLD",
        "reason": "因技术分析暂时不可用，采取保守策略",
        "stop_loss": price_data['price'] * 0.98,  # -2%
        "take_profit": price_data['price'] * 1.02,  # +2%
        "confidence": "LOW",
        "is_fallback": True
    }


def analyze_with_deepseek(price_data, symbol, state: StrategyState | None = None,
                          trade_config: dict | None = None,
                          exchange_instance=None):
    """使用DeepSeek分析市场并生成交易信号（增强版）"""
    cfg = trade_config or (state.trade_config if state else TRADE_CONFIG)
    ex = exchange_instance or (state.exchange if state else exchange)
    history_map = state.signal_history if state else signal_history

    # 生成技术分析文本
    technical_analysis = generate_technical_analysis_text(price_data)

    # 构建K线数据文本
    timeframe = cfg.get('timeframe', TRADE_CONFIG.get('timeframe', '15m'))
    kline_text = f"【最近5根{timeframe}K线数据】\n"
    for i, kline in enumerate(price_data['kline_data'][-5:]):
        trend = "阳线" if kline['close'] > kline['open'] else "阴线"
        change = ((kline['close'] - kline['open']) / kline['open']) * 100
        kline_text += f"K线{i + 1}: {trend} 开盘:{kline['open']:.2f} 收盘:{kline['close']:.2f} 涨跌:{change:+.2f}%\n"

    # 添加上次交易信号
    signal_text = ""
    history_list = history_map.setdefault(symbol, [])
    if history_list:
        last_signal = history_list[-1]
        signal_text = f"\n【上次交易信号】\n信号: {last_signal.get('signal', 'N/A')}\n信心: {last_signal.get('confidence', 'N/A')}"

    # 添加当前持仓信息
    current_pos = get_current_position(symbol, exchange_instance=ex)
    position_text = "无持仓" if not current_pos else f"{current_pos['side']}仓, 数量: {current_pos['size']}, 盈亏: {current_pos['unrealized_pnl']:.2f}USDT"

    prompt = f"""
    你是一个专业的加密货币交易分析师。请基于以下{symbol.replace('/USDT:USDT', '')}/USDT {timeframe}周期数据进行分析：

    {kline_text}

    {technical_analysis}

    {signal_text}

    【当前行情】
    - 当前价格: ${price_data['price']:,.2f}
    - 时间: {price_data['timestamp']}
    - 本K线最高: ${price_data['high']:,.2f}
    - 本K线最低: ${price_data['low']:,.2f}
    - 本K线成交量: {price_data['volume']:.2f}
    - 价格变化: {price_data['price_change']:+.2f}%
    - 当前持仓: {position_text}

    【分析要求】
    1. 基于{timeframe}K线趋势和技术指标给出交易信号: BUY(买入) / SELL(卖出) / HOLD(观望)
    2. 简要分析理由（考虑趋势连续性、支撑阻力、成交量等因素）
    3. 基于技术分析建议合理的止损价位
    4. 基于技术分析建议合理的止盈价位
    5. 评估信号信心程度

    【重要格式要求】
    - 必须返回纯JSON格式，不要有任何额外文本
    - 所有属性名必须使用双引号
    - 不要使用单引号
    - 不要添加注释
    - 确保JSON格式完全正确

    请用以下JSON格式回复：
    {{
        "signal": "BUY|SELL|HOLD",
        "reason": "分析理由",
        "stop_loss": 具体价格,
        "take_profit": 具体价格,
        "confidence": "HIGH|MEDIUM|LOW"
    }}
    """

    try:
        response = deepseek_client.chat.completions.create(
            model="deepseek-chat",
            messages=[
                {"role": "system",
                  "content": f"""你是一位资深的加密货币交易专家，拥有10年以上的实盘交易经验。你的交易理念是：

【核心交易哲学】
1. **生存第一**: 严格风险管理，永远把资本安全放在首位
2. **质量优于频率**: 要小亏大赚，不能大亏小赚
3. **趋势是你的朋友**: 永远顺着大趋势交易，不逆势操作
4. **耐心是美德**: 等待最佳时机，不急于入场
5. **纪律至上**: 严格执行交易计划，不受情绪影响

【专业能力】
- 精通技术分析：K线形态、支撑阻力、趋势线、技术指标
- 擅长风险管理：仓位控制、止损止盈、盈亏比计算
- 熟悉市场心理：识别贪婪与恐惧，避免情绪化交易
- 掌握多时间框架分析：{timeframe}周期与更大周期趋势结合

【分析原则】
- 寻找高概率、高盈亏比的交易机会
- 重视市场结构和价格行为
- 多重确认，避免单一指标依赖

请基于你的专业经验，为{timeframe}周期提供严谨的交易分析，并严格遵循JSON格式要求。"""},
                {"role": "user", "content": prompt}
            ],
            stream=False,
            temperature=0.1
        )

        # 安全解析JSON
        result = response.choices[0].message.content
        print(f"DeepSeek原始回复 for {symbol}: {result}")

        # 提取JSON部分
        start_idx = result.find('{')
        end_idx = result.rfind('}') + 1

        if start_idx != -1 and end_idx != 0:
            json_str = result[start_idx:end_idx]
            signal_data = safe_json_parse(json_str)

            if signal_data is None:
                signal_data = create_fallback_signal(price_data)
        else:
            signal_data = create_fallback_signal(price_data)

        # 验证必需字段
        required_fields = ['signal', 'reason', 'stop_loss', 'take_profit', 'confidence']
        if not all(field in signal_data for field in required_fields):
            signal_data = create_fallback_signal(price_data)

        # 保存信号到历史记录
        signal_data['timestamp'] = price_data['timestamp']
        history_list.append(signal_data)
        if len(history_list) > 30:
            history_list.pop(0)

        # 信号统计
        signal_count = len([s for s in history_list if s.get('signal') == signal_data['signal']])
        total_signals = len(history_list)
        print(f"{symbol} 信号统计: {signal_data['signal']} (最近{total_signals}次中出现{signal_count}次)")

        # 信号连续性检查
        if len(history_list) >= 3:
            last_three = [s['signal'] for s in history_list[-3:]]
            if len(set(last_three)) == 1:
                print(f"⚠️ 注意：{symbol} 连续3次{signal_data['signal']}信号")
                
                # 检查是否连续3次HOLD信号，发送Telegram提醒
                if signal_data['signal'] == 'HOLD' and len(history_list) >= 3:
                    hold_count = 0
                    recent_reasons = []
                    for i in range(len(history_list) - 1, -1, -1):
                        if history_list[i]['signal'] == 'HOLD':
                            hold_count += 1
                            # 收集最近3次的分析理由
                            if len(recent_reasons) < 3:
                                recent_reasons.append(history_list[i].get('reason', '无理由'))
                        else:
                            break
                    
                    if hold_count >= 3:
                        # 构建分析理由文本
                        reasons_text = ""
                        for i, reason in enumerate(reversed(recent_reasons), 1):
                            reasons_text += f"第{i}次: {reason}\n"
                        
                        hold_warning_msg = f"""
🔵 <b>连续观望提醒</b>

📊 <b>{symbol.replace('/USDT:USDT', '/USDT')}</b>
━━━━━━━━━━━━━━━━
⚠️ 已连续 <b>{hold_count}</b> 次 HOLD 信号

📈 当前价格: ${price_data['price']:,.2f}
📉 价格变化: {price_data['price_change']:+.2f}%

🤖 <b>DeepSeek 分析理由</b>
{reasons_text}

💭 <b>综合判断</b>
- 市场可能处于震荡整理阶段
- 趋势方向不够明确
- 缺乏高质量交易机会
- 建议耐心等待更好时机

🎯 <b>后续关注点</b>
- 密切观察价格突破方向
- 关注成交量变化
- 等待明确趋势信号
- 避免强行交易

⏰ 时间: {price_data['timestamp']}
"""
                        send_telegram_message(hold_warning_msg)

        return signal_data

    except Exception as e:
        print(f"DeepSeek分析 {symbol} 失败: {e}")
        return create_fallback_signal(price_data)


def calculate_position_value(state: StrategyState | None = None,
                             trade_config: dict | None = None,
                             exchange_instance=None):
    """计算当前所有持仓的总价值（保证金）"""
    total_margin = 0
    try:
        cfg = trade_config or (state.trade_config if state else TRADE_CONFIG)
        ex = exchange_instance or (state.exchange if state else exchange)
        if ex is None:
            print("计算持仓价值失败：交易所未初始化")
            return 0

        leverage = cfg.get('leverage', TRADE_CONFIG.get('leverage', 1))
        symbol_configs = cfg.get('symbols', [])

        for sym_config in symbol_configs:
            symbol = sym_config['symbol']
            pos = get_current_position(symbol, exchange_instance=ex)
            if pos:
                # 计算持仓占用的保证金
                margin = pos['size'] * pos['entry_price'] / leverage
                total_margin += margin
                print(f"  {symbol}: 持仓保证金 {margin:.2f} USDT")
    except Exception as e:
        print(f"计算持仓价值失败: {e}")
    return total_margin


def calculate_trading_amount(symbol, price, available_balance, trade_config=None):
    """根据资金管理规则计算建仓数量"""
    cfg = trade_config or TRADE_CONFIG

    # 获取配置的基础数量
    base_amount = None
    for sym in cfg.get('symbols', []):
        if sym['symbol'] == symbol:
            base_amount = sym['amount']
            break
    
    if base_amount is None:
        return 0
    
    # 计算单个交易对允许使用的最大资金
    max_position_value = available_balance * cfg.get('max_position_ratio', 1)
    
    # 计算基础数量需要的保证金
    leverage = cfg.get('leverage', TRADE_CONFIG.get('leverage', 1))
    base_margin = price * base_amount / leverage
    
    # 如果基础数量的保证金超过限制，按比例缩减
    if base_margin > max_position_value:
        adjusted_amount = (max_position_value * leverage) / price
        print(f"⚠️ 资金限制：调整数量从 {base_amount} 到 {adjusted_amount:.6f}")
        return adjusted_amount
    
    return base_amount


def normalize_amount_precision(symbol: str,
                               amount: float,
                               exchange_instance=None) -> tuple[float, Optional[float]]:
    """按交易所精度对齐数量，并返回最小交易量限制。"""
    ex = exchange_instance or exchange
    if ex is None or amount is None:
        return amount, None

    try:
        if not getattr(ex, 'markets', None):
            ex.load_markets()
        market = ex.market(symbol)
        min_amount = (
            market.get('limits', {})
            .get('amount', {})
            .get('min')
        )
        adjusted = float(ex.amount_to_precision(symbol, amount))
        if adjusted != amount:
            print(f"ℹ️ {symbol} 数量按精度调整为 {adjusted}")
        return adjusted, min_amount
    except Exception as e:
        print(f"⚠️ 无法对齐 {symbol} 数量精度: {e}")
        return amount, None


def calculate_realized_pnl(position: Optional[dict],
                           close_price: float,
                           signal_action: str) -> float:
    if not position:
        return 0.0

    side = position.get('side')
    entry_price = float(position.get('entry_price', 0) or 0)
    size = float(position.get('size', 0) or 0)
    if size <= 0 or entry_price <= 0:
        return 0.0

    closing = (
        (signal_action == 'BUY' and side == 'short') or
        (signal_action == 'SELL' and side == 'long')
    )
    if not closing:
        return 0.0

    if side == 'long':
        return (close_price - entry_price) * size
    return (entry_price - close_price) * size


def normalize_symbol_to_inst_id(symbol: str) -> Optional[str]:
    """将 ccxt 风格的符号转换为 OKX instId (如 BTC-USDT-SWAP)。"""
    try:
        clean_symbol = symbol or ''
        if ':' in clean_symbol:
            clean_symbol = clean_symbol.split(':')[0]
        base, quote = clean_symbol.split('/')
        return f"{base}-{quote}-SWAP"
    except ValueError:
        print(f"⚠️ 无法识别 {symbol} 的 instId")
        return None


def fetch_okx_order_limits(symbol: str,
                           price: float,
                           leverage: float,
                           exchange_instance=None) -> Optional[dict]:
    """查询 OKX 返回的最大可下单数量，避免触发保证金不足错误。"""
    ex = exchange_instance or exchange
    if ex is None:
        return None

    inst_id = normalize_symbol_to_inst_id(symbol)
    if inst_id is None:
        return None

    try:
        response = ex.private_get_account_max_size({
            'instId': inst_id,
            'tdMode': 'cross',
            'ccy': 'USDT',
            'px': str(price),
            'leverage': str(leverage),
        })
        data = response.get('data', []) if isinstance(response, dict) else []
        if not data:
            return None

        limits = data[0]
        def _to_float(value):
            try:
                return float(value)
            except (TypeError, ValueError):
                return 0.0

        return {
            'max_buy': _to_float(limits.get('maxBuy')),
            'max_sell': _to_float(limits.get('maxSell')),
        }
    except Exception as e:
        print(f"⚠️ 获取 {symbol} 交易限额失败: {e}")
        return None


def apply_okx_amount_limit(symbol: str,
                           action: str,
                           amount: float,
                           price: float,
                           leverage: float,
                           exchange_instance=None,
                           safety_ratio: Optional[float] = None,
                           risk_profile: str = 'balanced') -> tuple[float, str]:
    """确保下单数量不超过 OKX 返回的最大额度。"""
    limits = fetch_okx_order_limits(symbol, price, leverage, exchange_instance)
    if not limits:
        return amount, 'ok'

    limit_value = limits['max_buy'] if action == 'BUY' else limits['max_sell']
    action_label = '买入' if action == 'BUY' else '卖出'

    if limit_value <= 0:
        print(f"⚠️ OKX 当前返回 {symbol} 最大{action_label}数量为 0，跳过交易")
        return 0.0, 'skip'

    effective_ratio = safety_ratio if safety_ratio is not None else DEFAULT_LIMIT_SAFETY_RATIO
    safe_limit = limit_value * effective_ratio
    if safe_limit <= 0:
        print(f"⚠️ {symbol} 安全限额为 0，跳过交易")
        return 0.0, 'skip'

    if amount > safe_limit:
        print(
            f"⚠️ {symbol} 交易数量 {amount:.6f} 超出 OKX 限制 {safe_limit:.6f}，"
            f"基于 {risk_profile} 档缩减"
        )
        amount = safe_limit

    return amount, 'ok'


def execute_trade(
    signal_data,
    price_data,
    sym_config,
    state: StrategyState | None = None,
    trade_config: dict | None = None,
    exchange_instance=None,
    telegram_config: dict | None = None,
    sandbox: Optional[bool] = None,
    logger: logging.Logger | None = None,
):
    """执行交易 - OKX版本（增强资金管理）"""
    symbol = sym_config['symbol']
    cfg = trade_config or (state.trade_config if state else TRADE_CONFIG)
    ex = exchange_instance or (state.exchange if state else exchange)
    tele_cfg = telegram_config if telegram_config is not None else (state.telegram_config if state else None)
    sandbox_flag = sandbox if sandbox is not None else (state.sandbox if state else cfg.get('use_demo_api', True))
    positions_map = state.positions if state else positions
    account_id = state.account_id if state else None
    risk_profile = cfg.get('risk_profile', ACTIVE_RISK_PROFILE)

    if ex is None:
        msg = f"{symbol} 无法执行交易：交易所未初始化"
        print(msg)
        if logger:
            logger.error(msg)
        return {'executed': False, 'reason': 'exchange_not_initialized'}

    current_position = get_current_position(symbol, exchange_instance=ex)
    signal_action = signal_data.get('signal')

    print(f"\n{symbol} 交易信号: {signal_data['signal']}")
    print(f"信心程度: {signal_data['confidence']}")
    print(f"理由: {signal_data['reason']}")
    print(f"止损: ${signal_data['stop_loss']:,.2f}")
    print(f"止盈: ${signal_data['take_profit']:,.2f}")
    print(f"当前持仓: {current_position}")
    print(f"⚙️ 风险档位: {risk_profile}")

    # 风险管理：低信心信号不执行
    if signal_data.get('confidence') == 'LOW':
        print("⚠️ 低信心信号，跳过执行")
        return {'executed': False, 'reason': 'low_confidence'}

    if signal_action == 'HOLD':
        print("建议观望，不执行交易")
        return {'executed': False, 'reason': 'hold'}

    try:
        # 获取账户余额
        balance = ex.fetch_balance()
        usdt_balance = balance['USDT']['free']
        total_balance = balance['USDT']['total']
        
        print("\n💰 账户余额:")
        print(f"  可用余额: {usdt_balance:.2f} USDT")
        print(f"  总余额: {total_balance:.2f} USDT")
        
        # 计算当前总持仓价值
        current_total_margin = calculate_position_value(
            state=state,
            trade_config=cfg,
            exchange_instance=ex
        )
        usage_ratio = (current_total_margin / total_balance * 100) if total_balance else 0
        print(f"  当前总持仓保证金: {current_total_margin:.2f} USDT")
        print(f"  资金使用率: {usage_ratio:.1f}%")
        
        # 检查最小保留余额
        min_reserve = cfg.get('min_balance_reserve', TRADE_CONFIG.get('min_balance_reserve', 0))
        if usdt_balance < min_reserve:
            print(f"⚠️ 可用余额不足最小保留额 {min_reserve} USDT，跳过交易")
            return {'executed': False, 'reason': 'below_min_reserve'}
        
        # 检查总持仓比例限制
        total_ratio = cfg.get('total_position_ratio', TRADE_CONFIG.get('total_position_ratio', 1))
        max_total_margin = total_balance * total_ratio
        if current_total_margin >= max_total_margin:
            print(f"⚠️ 总持仓已达上限 ({total_ratio * 100:.1f}%)，跳过新建仓")
            return {'executed': False, 'reason': 'max_total_margin'}
        
        # 根据资金管理规则计算交易数量
        amount = calculate_trading_amount(
            symbol,
            price_data['price'],
            usdt_balance,
            trade_config=cfg
        )

        if amount <= 0:
            print("⚠️ 计算交易数量失败，跳过交易")
            return {'executed': False, 'reason': 'amount_not_positive'}

        amount, min_symbol_amount = normalize_amount_precision(symbol, amount, ex)
        print(f"📊 计算交易数量: {amount}")

        leverage = cfg.get('leverage', TRADE_CONFIG.get('leverage', 1))
        need_limit_check = (
            (signal_action == 'BUY' and (current_position is None or current_position['side'] == 'short')) or
            (signal_action == 'SELL' and (current_position is None or current_position['side'] == 'long'))
        )

        if need_limit_check:
            amount, limit_status = apply_okx_amount_limit(
                symbol,
                signal_action,
                amount,
                price_data['price'],
                leverage,
                exchange_instance=ex,
                safety_ratio=cfg.get('limit_safety_ratio', DEFAULT_LIMIT_SAFETY_RATIO),
                risk_profile=risk_profile,
            )
            if amount <= 0 or limit_status == 'skip':
                print("⚠️ 交易所可用额度不足，跳过本次交易")
                return {'executed': False, 'reason': 'exchange_limit'}

        amount, _ = normalize_amount_precision(symbol, amount, ex)
        if min_symbol_amount and amount < min_symbol_amount:
            print(
                f"⚠️ {symbol} 交易数量 {amount} 低于交易所最小数量 {min_symbol_amount}，"
                "跳过交易"
            )
            return {'executed': False, 'reason': 'below_exchange_min'}

        # 智能保证金检查和操作类型判断
        required_margin = 0
        operation_type = ""

        if signal_action == 'BUY':
            if current_position and current_position['side'] == 'short':
                required_margin = price_data['price'] * amount / leverage
                operation_type = "平空开多"
            elif not current_position:
                required_margin = price_data['price'] * amount / leverage
                operation_type = "开多仓"
                if current_total_margin + required_margin > max_total_margin:
                    print("⚠️ 建仓会超过总持仓限制，跳过交易")
                    return {'executed': False, 'reason': 'max_total_margin'}
            else:
                required_margin = 0
                operation_type = "保持多仓"

        elif signal_action == 'SELL':
            if current_position and current_position['side'] == 'long':
                required_margin = price_data['price'] * amount / leverage
                operation_type = "平多开空"
            elif not current_position:
                required_margin = price_data['price'] * amount / leverage
                operation_type = "开空仓"
                if current_total_margin + required_margin > max_total_margin:
                    print("⚠️ 建仓会超过总持仓限制，跳过交易")
                    return {'executed': False, 'reason': 'max_total_margin'}
            else:
                required_margin = 0
                operation_type = "保持空仓"
        else:
            print(f"⚠️ 未知信号 {signal_action}，跳过执行")
            return {'executed': False, 'reason': 'unknown_signal'}

        realized_pnl = calculate_realized_pnl(current_position, price_data['price'], signal_action)
        print(f"📝 操作类型: {operation_type}")
        print(f"💵 需要保证金: {required_margin:.2f} USDT")

        # 只有在需要额外保证金时才检查可用余额
        if required_margin > 0:
            available_for_trade = usdt_balance - min_reserve
            if required_margin > available_for_trade:
                print(f"⚠️ {symbol} 保证金不足")
                print(f"   需要: {required_margin:.2f} USDT")
                print(f"   可用: {available_for_trade:.2f} USDT (已扣除保留金)")
                send_error_notification(
                    f"保证金不足\n需要: {required_margin:.2f} USDT\n可用: {available_for_trade:.2f} USDT",
                    symbol,
                    trade_config=cfg,
                    telegram_config=tele_cfg,
                    sandbox=sandbox_flag,
                )
                return {'executed': False, 'reason': 'insufficient_margin'}
            print("✅ 保证金充足，可以执行")
        else:
            print("✅ 无需额外保证金，继续执行")
        
        # 准备账户信息用于通知
        balance_info = {
            'free': usdt_balance,
            'total': total_balance,
            'margin': current_total_margin,
            'usage': usage_ratio,
            'risk_profile': risk_profile,
        }
        
        # 发送交易信号通知
        send_trade_notification(
            symbol,
            signal_data,
            price_data,
            operation_type,
            amount,
            required_margin,
            balance_info,
            trade_config=cfg,
            telegram_config=tele_cfg,
            sandbox=sandbox_flag,
        )

        fee_rate = cfg.get('fee_rate', 0.0005)
        estimated_fee = price_data['price'] * amount * fee_rate

        # 执行交易逻辑   tag 是我的经纪商api（不拿白不拿），不会影响大家返佣，介意可以删除
        if signal_action == 'BUY':
            if current_position and current_position['side'] == 'short':
                print(f"{symbol} 平空仓并开多仓...")
                ex.create_market_order(
                    symbol,
                    'buy',
                    current_position['size'],
                    params={'reduceOnly': True, 'tag': 'f1ee03b510d5SUDE'}
                )
                time.sleep(1)
                ex.create_market_order(
                    symbol,
                    'buy',
                    amount,
                    params={'tag': 'f1ee03b510d5SUDE'}
                )
            elif current_position and current_position['side'] == 'long':
                print(f"{symbol} 已有多头持仓，保持现状")
            else:
                print(f"{symbol} 开多仓...")
                ex.create_market_order(
                    symbol,
                    'buy',
                    amount,
                    params={'tag': 'f1ee03b510d5SUDE'}
                )

        elif signal_action == 'SELL':
            if current_position and current_position['side'] == 'long':
                print(f"{symbol} 平多仓并开空仓...")
                ex.create_market_order(
                    symbol,
                    'sell',
                    current_position['size'],
                    params={'reduceOnly': True, 'tag': 'f1ee03b510d5SUDE'}
                )
                time.sleep(1)
                ex.create_market_order(
                    symbol,
                    'sell',
                    amount,
                    params={'tag': 'f1ee03b510d5SUDE'}
                )
            elif current_position and current_position['side'] == 'short':
                print(f"{symbol} 已有空头持仓，保持现状")
            else:
                print(f"{symbol} 开空仓...")
                ex.create_market_order(
                    symbol,
                    'sell',
                    amount,
                    params={'tag': 'f1ee03b510d5SUDE'}
                )

        print(f"{symbol} 订单执行成功")
        time.sleep(2)
        updated_position = get_current_position(symbol, exchange_instance=ex)
        positions_map[symbol] = updated_position
        print(f"{symbol} 更新后持仓: {updated_position}")
        
        # 发送持仓更新通知
        if updated_position:
            send_position_update(
                symbol,
                updated_position,
                updated_position.get('unrealized_pnl'),
                trade_config=cfg,
                telegram_config=tele_cfg,
                sandbox=sandbox_flag,
            )

        log_trade_event({
            'timestamp': datetime.utcnow().isoformat(),
            'account_id': account_id,
            'symbol': symbol,
            'signal': signal_action,
            'operation': operation_type,
            'amount': amount,
            'price': price_data['price'],
            'pnl': realized_pnl,
            'fee': estimated_fee,
            'margin_used': required_margin,
            'risk_profile': risk_profile,
            'balance_total': balance_info['total'],
            'environment': 'demo' if sandbox_flag else 'live',
        })

        return {
            'executed': True,
            'amount': amount,
            'operation': operation_type,
            'required_margin': required_margin,
            'pnl': realized_pnl,
            'fee': estimated_fee,
        }

    except Exception as e:
        error_msg = f"订单执行失败: {str(e)}"
        print(f"{symbol} {error_msg}")
        send_error_notification(
            error_msg,
            symbol,
            trade_config=cfg,
            telegram_config=tele_cfg,
            sandbox=sandbox_flag,
            account_id=state.account_id if state else None,
        )
        if logger:
            logger.exception(error_msg)
        import traceback
        traceback.print_exc()
        return {'executed': False, 'reason': 'exception', 'error': str(e)}


def analyze_with_deepseek_with_retry(price_data, symbol, max_retries=2):
    """带重试的DeepSeek分析"""
    for attempt in range(max_retries):
        try:
            signal_data = analyze_with_deepseek(price_data, symbol)
            if signal_data and not signal_data.get('is_fallback', False):
                return signal_data

            print(f"{symbol} 第{attempt + 1}次尝试失败，进行重试...")
            time.sleep(1)

        except Exception as e:
            print(f"{symbol} 第{attempt + 1}次尝试异常: {e}")
            if attempt == max_retries - 1:
                return create_fallback_signal(price_data)
            time.sleep(1)

    return create_fallback_signal(price_data)


def trading_bot():
    """主交易机器人函数"""
    print("\n" + "=" * 60)
    print(f"执行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 60)

    for sym_config in TRADE_CONFIG['symbols']:
        symbol = sym_config['symbol']
        print(f"\n处理 {symbol}...")

        # 1. 获取增强版K线数据
        price_data = get_ohlcv_enhanced(symbol)
        if not price_data:
            continue

        print(f"{symbol} 当前价格: ${price_data['price']:,.2f}")
        print(f"数据周期: {TRADE_CONFIG['timeframe']}")
        print(f"价格变化: {price_data['price_change']:+.2f}%")

        # 2. 使用DeepSeek分析（带重试）
        signal_data = analyze_with_deepseek_with_retry(price_data, symbol)

        if signal_data.get('is_fallback', False):
            print(f"{symbol} ⚠️ 使用备用交易信号")

        # 3. 执行交易
        execute_trade(signal_data, price_data, sym_config)


def main():
    """主函数"""
    print("=" * 60)
    print("🤖 OKX 自动交易机器人启动")
    print("=" * 60)
    print("支持多个符号: " + ", ".join([s['symbol'].replace('/USDT:USDT', '') for s in TRADE_CONFIG['symbols']]))
    print("融合技术指标策略 + DeepSeek AI 分析")
    print()

    # 检查是否通过环境变量设置模式（用于 Docker 非交互式运行）
    trade_mode_env = os.getenv('TRADE_MODE', '').lower()
    
    if trade_mode_env:
        # 通过环境变量设置（Docker 模式）
        print(f"检测到环境变量 TRADE_MODE={trade_mode_env}")
        if trade_mode_env in ['demo', 'test']:
            TRADE_CONFIG['use_demo_api'] = True
            print("\n✅ 已选择: OKX 模拟盘模式（环境变量）")
            print("   - 会连接 OKX 模拟盘 API")
            print("   - 可以真实下单到模拟账户")
            print("   - 不影响真实资金")
        elif trade_mode_env == 'live':
            TRADE_CONFIG['use_demo_api'] = False
            print("\n⚠️  已选择: OKX 实盘模式（环境变量）")
            print("   - 会连接 OKX 实盘 API")
            print("   - 会使用真实资金交易")
            print("   - 请务必谨慎操作！")
        else:
            print(f"\n⚠️ 无效的 TRADE_MODE 值: {trade_mode_env}，使用默认模拟盘模式")
            TRADE_CONFIG['use_demo_api'] = True
    else:
        # 交互式选择模式（本地运行）
        try:
            mode = input("请选择交易模式 (demo=模拟盘, live=实盘): ").strip().lower()
            if mode == 'demo' or mode == 'test':
                TRADE_CONFIG['use_demo_api'] = True
                print("\n✅ 已选择: OKX 模拟盘模式")
                print("   - 会连接 OKX 模拟盘 API")
                print("   - 可以真实下单到模拟账户")
                print("   - 不影响真实资金")
            elif mode == 'live':
                TRADE_CONFIG['use_demo_api'] = False
                print("\n⚠️  已选择: OKX 实盘模式")
                print("   - 会连接 OKX 实盘 API")
                print("   - 会使用真实资金交易")
                print("   - 请务必谨慎操作！")
                confirm = input("\n确认要使用实盘模式吗？(输入 YES 确认): ")
                if confirm != 'YES':
                    print("已取消，默认使用模拟盘模式")
                    TRADE_CONFIG['use_demo_api'] = True
            else:
                print("\n⚠️ 无效输入，默认使用模拟盘模式")
                TRADE_CONFIG['use_demo_api'] = True
        except (EOFError, KeyboardInterrupt):
            # Docker 中没有 tty 时会触发 EOFError
            print("\n检测到非交互式环境，使用默认模拟盘模式")
            TRADE_CONFIG['use_demo_api'] = True

    print(f"\n交易周期: {TRADE_CONFIG['timeframe']}")
    print(f"杠杆倍数: {TRADE_CONFIG['leverage']}x")
    print("已启用完整技术指标分析和持仓跟踪功能")
    print()

    # 初始化交易所
    try:
        init_exchange(use_demo=TRADE_CONFIG['use_demo_api'])
    except Exception as e:
        print(f"❌ 交易所初始化失败: {e}")
        print("\n请检查:")
        if TRADE_CONFIG['use_demo_api']:
            print("  - OKX_DEMO_API_KEY / OKX_API_KEY")
            print("  - OKX_DEMO_SECRET / OKX_SECRET")
            print("  - OKX_DEMO_PASSWORD / OKX_PASSWORD")
        else:
            print("  - OKX_API_KEY")
            print("  - OKX_SECRET")
            print("  - OKX_PASSWORD")
        return

    # 设置交易所参数
    if not setup_exchange():
        print("❌ 交易所设置失败，程序退出")
        return

    # 根据时间周期设置执行频率
    if TRADE_CONFIG['timeframe'] == '1h':
        schedule.every().hour.at(":01").do(trading_bot)
        print("执行频率: 每小时一次")
        interval_text = "每小时"
    elif TRADE_CONFIG['timeframe'] == '15m':
        schedule.every(15).minutes.do(trading_bot)
        print("执行频率: 每15分钟一次")
        interval_text = "每15分钟"
    else:
        schedule.every().hour.at(":01").do(trading_bot)
        print("执行频率: 每小时一次")
        interval_text = "每小时"
    
    # 发送启动通知
    env_tag = "🔵 模拟盘" if TRADE_CONFIG['use_demo_api'] else "🔴 实盘"
    symbols_text = ", ".join([s['symbol'].replace('/USDT:USDT', '') for s in TRADE_CONFIG['symbols']])
    startup_msg = f"""
{env_tag} <b>🤖 交易机器人已启动</b>

📊 <b>交易配置</b>
交易对: {symbols_text}
杠杆: {TRADE_CONFIG['leverage']}x
周期: {TRADE_CONFIG['timeframe']}
执行频率: {interval_text}

💰 <b>资金管理</b>
单个仓位上限: {TRADE_CONFIG['max_position_ratio']*100:.0f}%
总仓位上限: {TRADE_CONFIG['total_position_ratio']*100:.0f}%
保留余额: {TRADE_CONFIG['min_balance_reserve']} USDT

⏰ 启动时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

✅ 机器人运行中...
"""
    send_telegram_message(startup_msg)

    # 立即执行一次
    trading_bot()

    # 循环执行
    while True:
        schedule.run_pending()
        time.sleep(1)


if __name__ == "__main__":
    main()
